home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / dosrcss.zip / RCS.C < prev    next >
C/C++ Source or Header  |  1990-07-18  |  55KB  |  1,666 lines

  1. /*
  2.  *                      RCS create/change operation
  3.  */
  4. #ifndef lint
  5. static char rcsid[]=
  6. "$Header: /site/tmp/dosrcs/src/RCS/rcs.c,v 5.3 90/07/15 20:24:19 lfk Release $ Purdue CS";
  7. #endif
  8. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  9.    Distributed under license by the Free Software Foundation, Inc.
  10.  
  11. This file is part of RCS.
  12.  
  13. RCS is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 1, or (at your option)
  16. any later version.
  17.  
  18. RCS is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with RCS; see the file COPYING.  If not, write to
  25. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  
  27. Report problems and direct all questions to:
  28.  
  29.     rcs-bugs@cs.purdue.edu
  30.  
  31. */
  32.  
  33.  
  34.  
  35.  
  36. /* $Log:    rcs.c,v $
  37.  * Revision 5.3  90/07/15  20:24:19  lfk
  38.  * Most major fixes added between rev 5.1 and rev 5.5:
  39.  *     signals fixed so they work on MS-DOS
  40.  *     Added MKS arguments code so argv can be large
  41.  *     added code to handle slashes a'la Unix
  42.  *     added more file extensions to system from MS-DOS
  43.  * 
  44.  * Revision 5.2  90/07/15  11:32:15  ROOT_DOS
  45.  * DOS version of RCS 4.0 checked in for MODS
  46.  * by lfk@athena.mit.edu
  47.  * Also update to MSC 6.0
  48.  * 
  49.  * Revision 4.11  89/05/01  15:12:06  narten
  50.  * changed copyright header to reflect current distribution rules
  51.  * 
  52.  * Revision 4.10  88/11/08  16:01:54  narten
  53.  * didn't install previous patch correctly
  54.  * 
  55.  * Revision 4.9  88/11/08  13:56:01  narten
  56.  * removed include <sysexits.h> (not needed)
  57.  * minor fix for -A option
  58.  * 
  59.  * Revision 4.8  88/11/08  12:01:58  narten
  60.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  61.  * 
  62.  * Revision 4.8  88/08/09  19:12:27  eggert
  63.  * Don't access freed storage.
  64.  * Use execv(), not system(); yield proper exit status; remove lint.
  65.  * 
  66.  * Revision 4.7  87/12/18  11:37:17  narten
  67.  * lint cleanups (Guy Harris)
  68.  * 
  69.  * Revision 4.6  87/10/18  10:28:48  narten
  70.  * Updating verison numbers. Changes relative to 1.1 are actually 
  71.  * relative to 4.3
  72.  * 
  73.  * Revision 1.4  87/09/24  13:58:52  narten
  74.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  75.  * warnings)
  76.  * 
  77.  * Revision 1.3  87/03/27  14:21:55  jenkins
  78.  * Port to suns
  79.  * 
  80.  * Revision 1.2  85/12/17  13:59:09  albitz
  81.  * Changed setstate to rcs_setstate because of conflict with random.o.
  82.  * 
  83.  * Revision 1.1  84/01/23  14:50:09  kcs
  84.  * Initial revision
  85.  * 
  86.  * Revision 4.3  83/12/15  12:27:33  wft
  87.  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
  88.  * 
  89.  * Revision 4.2  83/12/05  10:18:20  wft
  90.  * Added conditional compilation for sending mail.
  91.  * Alternatives: V4_2BSD, V6, USG, and other.
  92.  * 
  93.  * Revision 4.1  83/05/10  16:43:02  wft
  94.  * Simplified breaklock(); added calls to findlock() and getcaller().
  95.  * Added option -b (default branch). Updated -s and -w for -b.
  96.  * Removed calls to stat(); now done by pairfilenames().
  97.  * Replaced most catchints() calls with restoreints().
  98.  * Removed check for exit status of delivermail().
  99.  * Directed all interactive output to stderr.
  100.  * 
  101.  * Revision 3.9.1.1  83/12/02  22:08:51  wft
  102.  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
  103.  * 
  104.  * Revision 3.9  83/02/15  15:38:39  wft
  105.  * Added call to fastcopy() to copy remainder of RCS file.
  106.  *
  107.  * Revision 3.8  83/01/18  17:37:51  wft
  108.  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
  109.  *
  110.  * Revision 3.7  83/01/15  18:04:25  wft
  111.  * Removed putree(); replaced with puttree() in rcssyn.c.
  112.  * Combined putdellog() and scanlogtext(); deleted putdellog().
  113.  * Cleaned up diagnostics and error messages. Fixed problem with
  114.  * mutilated files in case of deletions in 2 files in a single command.
  115.  * Changed marking of selector from 'D' to DELETE.
  116.  *
  117.  * Revision 3.6  83/01/14  15:37:31  wft
  118.  * Added ignoring of interrupts while new RCS file is renamed;
  119.  * Avoids deletion of RCS files by interrupts.
  120.  *
  121.  * Revision 3.5  82/12/10  21:11:39  wft
  122.  * Removed unused variables, fixed checking of return code from diff,
  123.  * introduced variant COMPAT2 for skipping Suffix on -A files.
  124.  *
  125.  * Revision 3.4  82/12/04  13:18:20  wft
  126.  * Replaced getdelta() with gettree(), changed breaklock to update
  127.  * field lockedby, added some diagnostics.
  128.  *
  129.  * Revision 3.3  82/12/03  17:08:04  wft
  130.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  131.  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
  132.  * fixed -u for missing revno. Disambiguated structure members.
  133.  *
  134.  * Revision 3.2  82/10/18  21:05:07  wft
  135.  * rcs -i now generates a file mode given by the umask minus write permission;
  136.  * otherwise, rcs keeps the mode, but removes write permission.
  137.  * I added a check for write error, fixed call to getlogin(), replaced
  138.  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
  139.  * conflicting, long identifiers.
  140.  *
  141.  * Revision 3.1  82/10/13  16:11:07  wft
  142.  * fixed type of variables receiving from getc() (char -> int).
  143.  */
  144.  
  145.  
  146. #include <sys/types.h>
  147. #include <sys/stat.h>
  148. #include "rcsbase.h"
  149. #ifndef lint
  150. static char rcsbaseid[] = RCSBASE;
  151. #endif
  152.  
  153. extern FILE * fopen();
  154. extern char * bindex();
  155. extern int  expandsym();                /* get numeric revision name        */
  156. extern struct  hshentry  * getnum();
  157. extern struct  lock      * addlock();   /* add a lock                       */
  158. extern char              * getid();
  159. extern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
  160. #ifdef COMPAT2
  161. extern char Ksuffix[];
  162. #endif
  163. extern char * getcaller();              /* get login of caller              */
  164. extern struct hshentry   * findlock();  /* find and remove lock             */
  165. extern struct hshentry   * genrevs();
  166. extern char * checkid();                /* check an identifier              */
  167. extern char * getfullRCSname();         /* get full path name of RCS file   */
  168. extern char * mktempfile();             /* temporary file name generator    */
  169. extern free();
  170. extern void catchints();
  171. extern void ignoreints();
  172. extern int  nerror;                     /* counter for errors               */
  173. extern int  quietflag;                  /* diagnoses suppressed if true     */
  174. extern char curlogmsg[];                /* current log message              */
  175. extern char * resultfile;               /* filename for fcopy            */
  176. extern FILE *fcopy;                     /* result file during editing       */
  177. extern FILE * finptr;                   /* RCS input file                   */
  178. extern FILE * frewrite;                 /* new RCS file                     */
  179. extern int    rewriteflag;              /* indicates whether input should be*/
  180.                     /* echoed to frewrite               */
  181.  
  182. char * newRCSfilename, * diffilename, * cutfilename;
  183. char * RCSfilename, * workfilename;
  184. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  185. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  186.  
  187. char accessorlst[strtsize];
  188. FILE * fcut;        /* temporary file to rebuild delta tree                 */
  189. int    oldumask;    /* save umask                                           */
  190.  
  191. int initflag, strictlock, strict_selected, textflag;
  192. char * textfile, * accessfile;
  193. char * caller, numrev[30];            /* caller's login;               */
  194. struct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
  195. struct  access  *curaccess,  *rmaccess;
  196. struct  hshentry        * gendeltas[hshsize];
  197.  
  198. #ifdef MSDOS
  199. extern char *gettmpdir();
  200. char    tmpdir[NCPPN];
  201. #endif /* MSDOS */
  202.  
  203. struct  Lockrev {
  204.         char    * revno;
  205.         struct  Lockrev   * nextrev;
  206. };
  207.  
  208. struct  Symrev {
  209.         char    * revno;
  210.         char    * ssymbol;
  211.         int     override;
  212.         struct  Symrev  * nextsym;
  213. };
  214.  
  215. struct  Status {
  216.         char    * revno;
  217.         char    * status;
  218.         struct  Status  * nextstatus;
  219. };
  220.  
  221. struct delrevpair {
  222.         char    * strt;
  223.         char    * end;
  224.         int     code;
  225. };
  226.  
  227. struct  Lockrev * newlocklst,   * rmvlocklst;
  228. struct  Symrev  * assoclst,  * lastassoc;
  229. struct  Status  * statelst,  * laststate;
  230. struct  delrevpair      * delrev;
  231. struct  hshentry        * cuthead,  *cuttail,  * delstrt;
  232. char    branchnum[revlength], * branchsym;
  233. struct  hshentry branchdummy;
  234. char    * commsyml;
  235. char    * headstate;
  236. int     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
  237. int     delaccessflag;
  238. enum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
  239.  
  240.  
  241. #ifdef MKS
  242. main(int argc, char *argv[], char *env[])
  243. #else
  244. main (argc, argv)
  245. int argc;
  246. char * argv[];
  247. #endif /* MKS */
  248. {
  249.         char    *comdusge;
  250.         int     result;
  251.     struct    access    *removeaccess(),  * getaccessor();
  252.         struct  Lockrev *rmnewlocklst();
  253.         struct  Lockrev *curlock,  * rmvlock, *lockpt;
  254.         struct  Status  * curstate;
  255.         struct  access    *temp, *temptr;
  256.     int status;
  257. #ifdef MKS
  258.     int z = 0;
  259.     int ARGC = 0;
  260.     char **tmp;
  261.     char *env_name;
  262. #    define MAXARGS 500        /* This means 500 items on the command line */
  263.     if (!(tmp = (char **)malloc(sizeof(char *)*(MAXARGS+1)))) {
  264.         fprintf(stderr, "%s: can't allocate space for arguments\n",argv[0]);
  265.         exit(1);
  266.     }
  267.     for ( z = 0 ; env[z] != NULL; z++) {    /* loop through environment */
  268.         if (*env[z] == '~') {    /* testing for entries begining with '~' */
  269.             *++env[z];            /* increment pointer to delete '~' */
  270.             tmp[ARGC++] = env[z];    /* add it to our new array */
  271.             if (z >= MAXARGS) {
  272.                 fprintf(stderr, "%s: can't handle any more arguments\n", argv[0]);
  273.                 goto list;
  274.             }
  275.         }
  276.         else if (*env[z] == '_') {    /* testing for entries begining with _ */
  277.             *++env[z] ; *++env[z];    /* move past the '_' and the '=' */
  278.             env_name = env[z];    /* copy the name */
  279.         }
  280.     }
  281. list:
  282.     if ( STREQ( (char *) argv[0] , env_name ) ) {    /* test name against startup args */
  283.         /* environment arguments meant for this program */
  284. #    ifdef DEBUG
  285.         printf("Using shell supplied args\n");
  286. #    endif
  287.         argc = ARGC;
  288.         tmp[ARGC] = NULL;    /* the terminal NULL */
  289.         argv = tmp;
  290.     }
  291. #    ifdef DEBUG
  292.     else 
  293.         printf("Using startup supplied args\n");
  294. #    endif /* debug */
  295. #endif /* MKS */
  296.  
  297.     status = 0;
  298.         nerror = 0;
  299.     catchints();
  300.         cmdid = "rcs";
  301.         quietflag = false;
  302.         comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
  303.         rplaccessor = nil;     delstrt = nil;
  304.         accessfile = textfile = caller = nil;
  305.         branchflag = commentflag = chgheadstate = false;
  306.         lockhead = false; unlockcaller=false;
  307.         initflag= textflag = false;
  308.         strict_selected = 0;
  309.  
  310.     caller=getcaller();
  311.         laststate = statelst = nil;
  312.         lastassoc = assoclst = nil;
  313.         curlock = rmvlock = newlocklst = rmvlocklst = nil;
  314.         curaccess = rmaccess = rmvaccessor = newaccessor = nil;
  315.         delaccessflag = false;
  316.  
  317.         /*  preprocessing command options    */
  318.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  319.                 switch ((*argv)[1]) {
  320.  
  321.                 case 'i':   /*  initail version  */
  322.                         initflag = true;
  323.                         break;
  324.  
  325.                 case 'b':  /* change default branch */
  326.                         if (branchflag)warn("Redfinition of option -b");
  327.                         branchflag= true;
  328.                         branchsym = (*argv)+2;
  329.                         break;
  330.  
  331.                 case 'c':   /*  change comment symbol   */
  332.                         if (commentflag)warn("Redefinition of option -c");
  333.                         commentflag = true;
  334.                         commsyml = (*argv)+2;
  335.                         break;
  336.  
  337.                 case 'a':  /*  add new accessor   */
  338.                         if ( (*argv)[2] == '\0') {
  339.                             error("Login name missing after -a");
  340.                         }
  341.                         if ( (temp = getaccessor((*argv)+1)) ) {
  342.                             if ( newaccessor )
  343.                                 curaccess->nextaccess = temp->nextaccess;
  344.                             else
  345.                                 newaccessor = temp->nextaccess;
  346.                             temp->nextaccess = nil;
  347.                             curaccess = temp;
  348.                         }
  349.                         break;
  350.  
  351.                 case 'A':  /*  append access list according to accessfile  */
  352.                         if ( (*argv)[2] == '\0') {
  353.                             error("Missing file name after -A");
  354.                             break;
  355.                         }
  356.                         if ( accessfile) warn("Redefinition of option -A");
  357.                         *argv = *argv+2;
  358.                         if( pairfilenames(1, argv, true, false) > 0) {
  359.                             releaselst(newaccessor);
  360.                             newaccessor = curaccess = nil;
  361.                             releaselst(rmvaccessor);
  362.                             rmvaccessor = rmaccess = nil;
  363.                             accessfile = RCSfilename;
  364.                         }
  365.                         else
  366.                             accessfile = nil;
  367.                         break;
  368.  
  369.                 case 'e':    /*  remove accessors   */
  370.                         if ( (*argv)[2] == '\0' ) {
  371.                             delaccessflag = true;
  372.                             break;
  373.                         }
  374.                         if ( (temp = getaccessor((*argv)+1))  ) {
  375.                             if ( rmvaccessor )
  376.                                 rmaccess->nextaccess = temp->nextaccess;
  377.                             else
  378.                                 rmvaccessor = temp->nextaccess;
  379.                             temptr = temp->nextaccess;
  380.                             temp->nextaccess = nil;
  381.                             rmaccess = temp;
  382.                             while( temptr ) {
  383.                                 newaccessor = removeaccess(temptr,newaccessor,false);
  384.                                 temptr = temptr->nextaccess;
  385.                             }
  386.                             curaccess = temp = newaccessor;
  387.                             while( temp){
  388.                                 curaccess = temp;
  389.                                 temp = temp->nextaccess;
  390.                             }
  391.                         }
  392.                         break;
  393.  
  394.                 case 'l':    /*   lock a revision if it is unlocked   */
  395.                         if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
  396.                             lockhead = true;
  397.                             break;
  398.                         }
  399.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  400.                         lockpt->revno = (*argv)+2;
  401.                         lockpt->nextrev = nil;
  402.                         if ( curlock )
  403.                             curlock->nextrev = lockpt;
  404.                         else
  405.                             newlocklst = lockpt;
  406.                         curlock = lockpt;
  407.                         break;
  408.  
  409.                 case 'u':   /*  release lock of a locked revision   */
  410.                         if ( (*argv)[2] == '\0'){ /*  unlock head  */
  411.                             unlockcaller=true;
  412.                             break;
  413.                         }
  414.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  415.                         lockpt->revno = (*argv)+2;
  416.                         lockpt->nextrev = nil;
  417.                         if (rmvlock)
  418.                             rmvlock->nextrev = lockpt;
  419.                         else
  420.                             rmvlocklst = lockpt;
  421.                         rmvlock = lockpt;
  422.  
  423.                         curlock = rmnewlocklst(lockpt);
  424.                         break;
  425.  
  426.                 case 'L':   /*  set strict locking */
  427.                         if (strict_selected++) {  /* Already selected L or U? */
  428.                if (!strictlock)      /* Already selected -U? */
  429.                    warn("Option -L overrides -U");
  430.                         }
  431.                         strictlock = true;
  432.                         break;
  433.  
  434.                 case 'U':   /*  release strict locking */
  435.                         if (strict_selected++) {  /* Already selected L or U? */
  436.                if (strictlock)      /* Already selected -L? */
  437.                    warn("Option -L overrides -U");
  438.                         }
  439.             else
  440.                 strictlock = false;
  441.                         break;
  442.  
  443.                 case 'n':    /*  add new association: error, if name exists */
  444.                         if ( (*argv)[2] == '\0') {
  445.                             error("Missing symbolic name after -n");
  446.                             break;
  447.                         }
  448.                         getassoclst(false, (*argv)+1);
  449.                         break;
  450.  
  451.                 case 'N':   /*  add or change association   */
  452.                         if ( (*argv)[2] == '\0') {
  453.                             error("Missing symbolic name after -N");
  454.                             break;
  455.                         }
  456.                         getassoclst(true, (*argv)+1);
  457.                         break;
  458.  
  459.                 case 'o':   /*  delete revisins  */
  460.                         if (delrev) warn("Redefinition of option -o");
  461.                         if ( (*argv)[2] == '\0' ) {
  462.                             error("Missing revision range after -o");
  463.                             break;
  464.                         }
  465.                         getdelrev( (*argv)+1 );
  466.                         break;
  467.  
  468.                 case 's':   /*  change state attribute of a revision  */
  469.                         if ( (*argv)[2] == '\0') {
  470.                             error("State missing after -s");
  471.                             break;
  472.                         }
  473.                         getstates( (*argv)+1);
  474.                         break;
  475.  
  476.                 case 't':   /*  change descriptive text   */
  477.                         textflag=true;
  478.                         if ((*argv)[2]!='\0'){
  479.                                 if (textfile!=nil)warn("Redefinition of -t option");
  480.                                 textfile = (*argv)+2;
  481.                         }
  482.                         break;
  483.  
  484.                 case 'q':
  485.                         quietflag = true;
  486.                         break;
  487.                 default:
  488.                         faterror("Unknown option: %s\n%s", *argv, comdusge);
  489.                 };
  490.         }  /* end processing of options */
  491.  
  492.         if (argc<1) faterror("No input file\n%s", comdusge);
  493.         if (nerror) {   /*  exit, if any error in command options  */
  494.             diagnose("%s aborted",cmdid);
  495.             exit(1);
  496.         }
  497.         if (accessfile) /*  get replacement for access list   */
  498.             getrplaccess();
  499.         if (nerror) {
  500.             diagnose("%s aborted",cmdid);
  501.             exit(1);
  502.         }
  503. #ifdef MSDOS
  504.     strcpy( tmpdir, gettmpdir() );
  505. #endif /* MSDOS */        
  506.     /* now handle all filenames */
  507.         do {
  508.         rewriteflag = false;
  509.         finptr=frewrite=NULL;
  510.  
  511.         if ( initflag ) {
  512.             switch( pairfilenames(argc, argv, false, false) ) {
  513.                 case -1: break;        /*  not exist; ok */
  514.                 case  0: continue;     /*  error         */
  515.                 case  1: error("file %s exists already", RCSfilename);
  516.                          VOID fclose(finptr);
  517.                          continue;
  518.             }
  519.     }
  520.         else  {
  521.             switch( pairfilenames(argc, argv, true, false) ) {
  522.                 case -1: continue;    /*  not exist      */
  523.                 case  0: continue;    /*  errors         */
  524.                 case  1: break;       /*  file exists; ok*/
  525.             }
  526.     }
  527.  
  528.  
  529.         /* now RCSfilename contains the name of the RCS file, and
  530.          * workfilename contains the name of the working file.
  531.          * if !initflag, finptr contains the file descriptor for the
  532.          * RCS file. The admin node is initialized.
  533.          */
  534.  
  535.         diagnose("RCS file: %s", RCSfilename);
  536.  
  537.         if (!trydiraccess(RCSfilename))            continue; /* give up */
  538.         if (!initflag && !checkaccesslist(caller)) continue; /* give up */
  539.         if (!trysema(RCSfilename,true))            continue; /* give up */
  540.  
  541.         gettree(); /* read in delta tree */
  542.  
  543.         /*  update admin. node    */
  544.         if (strict_selected) StrictLocks = strictlock;
  545.         if (commentflag) Comment = commsyml;
  546.  
  547.         /* update default branch */
  548.         if (branchflag && expandsym(branchsym, branchnum)) {
  549.             if (countnumflds(branchnum)>0) {
  550.                 branchdummy.num=branchnum;
  551.                 Dbranch = &branchdummy;
  552.             } else
  553.                 Dbranch = nil;
  554.         }
  555.  
  556.         /*  update access list   */
  557.         if ( delaccessflag ) AccessList = nil;
  558.         if ( accessfile ) {
  559.             temp = rplaccessor;
  560.             while( temp ) {
  561.                 temptr = temp->nextaccess;
  562.                 if ( addnewaccess(temp) )
  563.                     temp->nextaccess = nil;
  564.                 temp = temptr;
  565.             }
  566.         }
  567.         temp = rmvaccessor;
  568.         while(temp)   {         /*  remove accessors from accesslist   */
  569.             AccessList = removeaccess(temp, AccessList,true);
  570.             temp = temp->nextaccess;
  571.         }
  572.         temp = newaccessor;
  573.         while( temp)  {         /*  add new accessors   */
  574.             temptr = temp->nextaccess;
  575.             if ( addnewaccess( temp ) )
  576.                 temp->nextaccess = nil;
  577.             temp = temptr;
  578.         }
  579.  
  580.         updateassoc();          /*  update association list   */
  581.  
  582.         updatelocks();          /*  update locks              */
  583.  
  584.         /*  update state attribution  */
  585.         if (chgheadstate) {
  586.             /* change state of default branch or head */
  587.             if (Dbranch==nil) {
  588.                 if (Head==nil)
  589.                      warn("Can't change states in an empty tree");
  590.                 else Head->state = headstate;
  591.             } else {
  592.                 rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
  593.             }
  594.         }
  595.         curstate = statelst;
  596.         while( curstate ) {
  597.             rcs_setstate(curstate->revno,curstate->status);
  598.             curstate = curstate->nextstatus;
  599.         }
  600.  
  601.         cuthead = cuttail = nil;
  602.         if ( delrev && removerevs()) {
  603.             /*  rebuild delta tree if some deltas are deleted   */
  604.             if ( cuttail )
  605.         VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
  606.                  (char *)nil, gendeltas);
  607.             buildtree();
  608.         }
  609.  
  610.  
  611.         /* prepare for rewriting the RCS file */
  612. #ifdef MSDOS
  613.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  614.         oldumask = umask(S_IWRITE); /* turn off write bits */
  615. #else
  616.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  617.         oldumask = umask(0222); /* turn off write bits */
  618. #endif /* MSDOS */
  619.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  620.                 VOID fclose(finptr);
  621.                 error("Can't open file %s",newRCSfilename);
  622.                 continue;
  623.         }
  624.         VOID umask(oldumask);
  625.         putadmin(frewrite);
  626.         if ( Head )
  627.            puttree(Head, frewrite);
  628.     putdesc(initflag,textflag,textfile,quietflag);
  629.         rewriteflag = false;
  630.  
  631.         if ( Head) {
  632.             if (!delrev) {
  633.                 /* no revision deleted */
  634.                 fastcopy(finptr,frewrite);
  635.             } else {
  636.                 if ( cuttail )
  637.                     buildeltatext(gendeltas);
  638.                 else
  639.                     scanlogtext((struct hshentry *)nil,empty);
  640.                     /* copy rest of delta text nodes that are not deleted      */
  641.             }
  642.         }
  643.         ffclose(frewrite);   frewrite = NULL;
  644.         if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
  645.         ignoreints();        /* ignore interrupts */
  646. #ifdef MSDOS
  647.             if(Rename(newRCSfilename,RCSfilename)<0) {
  648. #else
  649.             if(rename(newRCSfilename,RCSfilename)<0) {
  650. #endif /* MSDOS */
  651.                 error("Can't create RCS file %s; saved in %s",
  652.                    RCSfilename, newRCSfilename);
  653.                 newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
  654.                 restoreints();
  655.                 VOID cleanup();
  656.                 break;
  657.             }
  658.             newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  659.             /* update mode */
  660.             result=0;
  661.             if (!initflag) /* preserve mode bits */
  662.                 result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
  663.             elsif (haveworkstat==0)  /* initialization, and work file exists */
  664.                 result=chmod(RCSfilename,workstat.st_mode & ~0222);
  665.             if (result<0) warn("Can't set mode of %s",RCSfilename);
  666.  
  667.             restoreints();                /* catch them all again */
  668.             diagnose("done");
  669.         } else {
  670.         diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
  671.         status = 1;
  672.         nerror = 0;
  673.         }
  674.         } while (cleanup(),
  675.                  ++argv, --argc >=1);
  676.  
  677.         exit(status);
  678. }       /* end of main (rcs) */
  679.  
  680.  
  681.  
  682. getassoclst(flag, sp)
  683. int     flag;
  684. char    * sp;
  685. /*  Function:   associate a symbolic name to a revision or branch,      */
  686. /*              and store in assoclst                                   */
  687.  
  688. {
  689.         struct   Symrev  * pt;
  690.         char             * temp, *temp2;
  691.         int                c;
  692.  
  693.         while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
  694.         temp = sp;
  695.         temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
  696.         sp = temp2; c = *sp;   *sp = '\0';
  697.         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
  698.  
  699.         if ( c != ':' && c != '\0') {
  700.         error("Invalid string %s after option -n or -N",sp);
  701.             return;
  702.         }
  703.  
  704.         pt = (struct Symrev *)talloc(sizeof(struct Symrev));
  705.         pt->ssymbol = temp;
  706.         pt->override = flag;
  707.     if (c == '\0')  /*  delete symbol  */
  708.             pt->revno = nil;
  709.         else {
  710.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  711.         if ( c == '\0' )
  712.                 pt->revno = nil;
  713.         else
  714.                 pt->revno = sp;
  715.         }
  716.         pt->nextsym = nil;
  717.         if (lastassoc)
  718.             lastassoc->nextsym = pt;
  719.         else
  720.             assoclst = pt;
  721.         lastassoc = pt;
  722.         return;
  723. }
  724.  
  725.  
  726.  
  727. struct access * getaccessor( sp)
  728. char            *sp;
  729. /*   Function:  get the accessor list of options -e and -a,     */
  730. /*              and store in curpt                              */
  731.  
  732.  
  733. {
  734.         struct  access  * curpt, * pt,  *pre;
  735.         char    *temp;
  736.         register c;
  737.  
  738.         while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
  739.         if ( c == '\0') {
  740.             error("Missing login name after option -a or -e");
  741.             return nil;
  742.         }
  743.  
  744.         curpt = pt = nil;
  745.         while( c != '\0') {
  746.                 temp=checkid(sp,',');
  747.                 pt = (struct access *)talloc(sizeof(struct access));
  748.                 pt->login = sp;
  749.                 if ( curpt )
  750.                     pre->nextaccess = pt;
  751.                 else
  752.                     curpt = pt;
  753.                 pt->nextaccess = curpt;
  754.                 pre = pt;
  755.                 sp = temp;    c = *sp;   *sp = '\0';
  756.                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
  757.         }
  758.         return pt;
  759. }
  760.  
  761.  
  762.  
  763. getstates(sp)
  764. char    *sp;
  765. /*   Function:  get one state attribute and the corresponding   */
  766. /*              revision and store in statelst                  */
  767.  
  768. {
  769.         char    *temp, *temp2;
  770.         struct  Status  *pt;
  771.         register        c;
  772.  
  773.         while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
  774.         temp = sp;
  775.         temp2=checkid(sp,':');  /* check for invalid state attribute */
  776.         sp = temp2;   c = *sp;   *sp = '\0';
  777.         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
  778.  
  779.         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
  780.             chgheadstate = true;
  781.             headstate  = temp;
  782.             return;
  783.         }
  784.         else if ( c != ':' ) {
  785.             error("Missing ':' after state in option -s");
  786.             return;
  787.         }
  788.  
  789.         while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
  790.         pt = (struct Status *)talloc(sizeof(struct Status));
  791.         pt->status     = temp;
  792.         pt->revno      = sp;
  793.         pt->nextstatus = nil;
  794.         if (laststate)
  795.             laststate->nextstatus = pt;
  796.         else
  797.             statelst = pt;
  798.         laststate = pt;
  799. }
  800.  
  801.  
  802.  
  803. getrplaccess()
  804. /*   Function : get the accesslist of the 'accessfile'  */
  805. /*              and place in rplaccessor                */
  806. {
  807.         register        char    *id, *nextp;
  808.         struct          access  *newaccess, *oldaccess;
  809.  
  810.         if ( (finptr=fopen(accessfile, "r")) == NULL) {
  811.             faterror("Can't open file %s", accessfile);
  812.         }
  813.         Lexinit();
  814.         nextp = &accessorlst[0];
  815.  
  816.         if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
  817.         VOID getnum();
  818.         if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
  819.  
  820.         if (getkey(Kbranch)) { /* optional */
  821.                 Dbranch=getnum();
  822.                 if (!getlex(SEMI)) serror("Missing ';' after branch list");
  823.         }
  824.  
  825.  
  826. #ifdef COMPAT2
  827.         /* read suffix. Only in release 2 format */
  828.         if (getkey(Ksuffix)) {
  829.             if (nexttok==STRING) {
  830.                 readstring(); nextlex(); /*through away the suffix*/
  831.             } elsif(nexttok==ID) {
  832.                 nextlex();
  833.             }
  834.             if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
  835.         }
  836. #endif
  837.  
  838.         if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
  839.         oldaccess = nil;
  840.         while( id =getid() ) {
  841.             newaccess = (struct access *)talloc(sizeof(struct access));
  842.             newaccess->login = nextp;
  843.             newaccess->nextaccess = nil;
  844.             while( ( *nextp++ = *id++) != '\0')  ;
  845.             if ( oldaccess )
  846.                 oldaccess->nextaccess = newaccess;
  847.             else
  848.                 rplaccessor = newaccess;
  849.             oldaccess = newaccess;
  850.         }
  851.         if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
  852.         return;
  853. }
  854.  
  855.  
  856.  
  857. getdelrev(sp)
  858. char    *sp;
  859. /*   Function:  get revision range or branch to be deleted,     */
  860. /*              and place in delrev                             */
  861. {
  862.         int    c;
  863.         struct  delrevpair      *pt;
  864.  
  865.         if (delrev) free((char *)delrev);
  866.  
  867.         pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
  868.         while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
  869.  
  870.         if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
  871.             while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
  872.             pt->strt = sp;    pt->code = 1;
  873.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
  874.             *sp = '\0';
  875.             pt->end = nil;  delrev = pt;
  876.             return;
  877.         }
  878.         else {
  879.             pt->strt = sp;
  880.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
  881.                    && c != '-' && c != '<' )  c = *++sp;
  882.             *sp = '\0';
  883.             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
  884.             if ( c == '\0' )  {  /*   -o rev or branch   */
  885.                 pt->end = nil;   pt->code = 0;
  886.                 delrev = pt;
  887.                 return;
  888.             }
  889.             if ( c != '-' && c != '<') {
  890.                 faterror("Invalid range %s %s after -o", pt->strt, sp);
  891.                 free((char *)pt);
  892.                 return;
  893.             }
  894.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  895.             if ( c == '\0') {  /*  -o   rev-   or   rev<   */
  896.                 pt->end = nil;   pt->code = 2;
  897.                 delrev = pt;
  898.                 return;
  899.             }
  900.         }
  901.         /*   -o   rev1-rev2    or   rev1<rev2   */
  902.         pt->end = sp;  pt->code = 3;   delrev = pt;
  903.         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
  904.         *sp = '\0';
  905. }
  906.  
  907.  
  908.  
  909.  
  910. scanlogtext(delta,func)
  911. struct hshentry * delta; enum stringwork func;
  912. /* Function: Scans delta text nodes up to and including the one given
  913.  * by delta, or up to last one present, if delta==nil.
  914.  * For the one given by delta (if delta!=nil), the log message is saved into
  915.  * curlogmsg and the text is processed according to parameter func.
  916.  * Assumes the initial lexeme must be read in first.
  917.  * Does not advance nexttok after it is finished, except if delta==nil.
  918.  */
  919. {       struct hshentry * nextdelta;
  920.  
  921.         do {
  922.                 rewriteflag = false;
  923.                 nextlex();
  924.                 if (!(nextdelta=getnum())) {
  925.                     if(delta)
  926.                         faterror("Can't find delta for revision %s", delta->num);
  927.                     else return; /* no more delta text nodes */
  928.                 }
  929.                 if ( nextdelta->selector != DELETE) {
  930.                         rewriteflag = true;
  931.                         VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
  932.                 }
  933.                 if (!getkey(Klog) || nexttok!=STRING)
  934.                         serror("Missing log entry");
  935.                 elsif (delta==nextdelta) {
  936.                         VOID savestring(curlogmsg,logsize);
  937.                         delta->log=curlogmsg;
  938.                 } else {readstring();
  939.                         if (delta!=nil) delta->log="";
  940.                 }
  941.                 nextlex();
  942.                 if (!getkey(Ktext) || nexttok!=STRING)
  943.                         fatserror("Missing delta text");
  944.  
  945.                 if(delta==nextdelta)
  946.                         /* got the one we're looking for */
  947.                         switch (func) {
  948.                         case copy:      copystring();
  949.                                         break;
  950.                         case edit:      editstring((struct hshentry *)nil);
  951.                                         break;
  952.                         default:        faterror("Wrong scanlogtext");
  953.                         }
  954.                 else    readstring(); /* skip over it */
  955.  
  956.         } while (delta!=nextdelta);
  957. }
  958.  
  959.  
  960.  
  961. releaselst(sourcelst)
  962. struct  access  * sourcelst;
  963. /*   Function:  release the storages whose address are in sourcelst   */
  964.  
  965. {
  966.         struct  access  * pt;
  967.  
  968.         pt = sourcelst;
  969.         while(pt) {
  970.         struct access *pn = pt->nextaccess;
  971.             free((char *)pt);
  972.             pt = pn;
  973.         }
  974. }
  975.  
  976.  
  977.  
  978. struct  Lockrev  * rmnewlocklst(which)
  979. struct  Lockrev  * which;
  980. /*   Function:  remove lock to revision which->revno from newlocklst   */
  981.  
  982. {
  983.         struct  Lockrev   * pt, *pre;
  984.  
  985.         while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
  986.         struct Lockrev *pn = newlocklst->nextrev;
  987.             free((char *)newlocklst);
  988.         newlocklst = pn;
  989.         }
  990.  
  991.         pt = pre = newlocklst;
  992.         while( pt ) {
  993.             if ( ! strcmp(pt->revno, which->revno) ) {
  994.                 pre->nextrev = pt->nextrev;
  995.                 free((char *)pt);
  996.         pt = pre->nextrev;
  997.             }
  998.             else {
  999.                 pre = pt;
  1000.                 pt = pt->nextrev;
  1001.             }
  1002.         }
  1003.         return pre;
  1004. }
  1005.  
  1006.  
  1007.  
  1008. struct  access  * removeaccess( who, sourcelst,flag)
  1009. struct  access  * who, * sourcelst;
  1010. int     flag;
  1011. /*   Function:  remove the accessor-- who from sourcelst     */
  1012.  
  1013. {
  1014.         struct  access  *pt, *pre;
  1015.  
  1016.         pt = sourcelst;
  1017.         while( pt && (! strcmp(who->login, pt->login) )) {
  1018.         pre = pt->nextaccess;
  1019.             free((char *)pt);
  1020.         pt = pre;
  1021.             flag = false;
  1022.     }
  1023.         pre = sourcelst = pt;
  1024.         while( pt ) {
  1025.             if ( ! strcmp(who->login, pt->login) ) {
  1026.         pre->nextaccess = pt->nextaccess;
  1027.         free((char *)pt);
  1028.         pt = pre->nextaccess;
  1029.                 flag = false;
  1030.             }
  1031.             else {
  1032.                 pre = pt;
  1033.                 pt = pt->nextaccess;
  1034.             }
  1035.         }
  1036.         if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
  1037.         return sourcelst;
  1038. }
  1039.  
  1040.  
  1041.  
  1042. int addnewaccess( who )
  1043. struct  access  * who;
  1044. /*   Function:  add new accessor-- who into AccessList    */
  1045.  
  1046. {
  1047.         struct  access  *pt,  *pre;
  1048.  
  1049.         pre = pt = AccessList;
  1050.  
  1051.         while( pt ) {
  1052.             if ( strcmp( who->login, pt->login) ) {
  1053.                 pre = pt;
  1054.                 pt = pt->nextaccess;
  1055.             }
  1056.             else
  1057.                 return 0;
  1058.         }
  1059.         if ( pre == pt )
  1060.             AccessList = who;
  1061.         else
  1062.             pre->nextaccess = who;
  1063.         return 1;
  1064. }
  1065.  
  1066.  
  1067. sendmail(Delta, who)
  1068. char    * Delta,  *who;
  1069. /*   Function:  mail to who, informing him that his lock on delta was
  1070.  *   broken by caller. Ask first whether to go ahead. Return false on
  1071.  *   error or if user decides not to break the lock.
  1072.  */
  1073. {
  1074.         char    * messagefile;
  1075.         int   old1, old2, c, response;
  1076.         FILE    * mailmess;
  1077.  
  1078.  
  1079.     VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
  1080.         VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
  1081.         response=c=getchar();
  1082.         while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  1083.         if (response=='\n'||response=='n'||response=='N') return false;
  1084.  
  1085.         /* go ahead with breaking  */
  1086. #ifdef MSDOS
  1087.         messagefile=mktempfile( tmpdir, "mmXXXXXX");
  1088. #else
  1089.         messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
  1090. #endif /* MSDOS */
  1091.         if ( (mailmess = fopen(messagefile, "w")) == NULL) {
  1092.             faterror("Can't open file %s", messagefile);
  1093.         }
  1094.  
  1095.     VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
  1096.         VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
  1097.         VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
  1098.         VOID fputs("State the reason for breaking the lock:\n", stderr);
  1099.         VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
  1100.  
  1101.         old1 = '\n';    old2 = ' ';
  1102.         for (; ;) {
  1103.             c = getchar();
  1104.             if ( c == EOF ) {
  1105.                 VOID putc('\n',stderr);
  1106.                 VOID fprintf(mailmess, "%c\n", old1);
  1107.                 break;
  1108.             }
  1109.             else if ( c == '\n' && old1 == '.' && old2 == '\n')
  1110.                 break;
  1111.             else {
  1112.                 VOID fputc( old1, mailmess);
  1113.                 old2 = old1;   old1 = c;
  1114.                 if (c== '\n') VOID fputs(">> ", stderr);
  1115.             }
  1116.         }
  1117.         ffclose(mailmess);
  1118.  
  1119.     /* ignore the exit status, even if delivermail unsuccessful */
  1120.         VOID run(messagefile,(char*)nil,
  1121. #ifdef SENDMAIL
  1122.         "/usr/lib/sendmail",
  1123. #else
  1124. #  ifdef DELIVERMAIL
  1125.         "/etc/delivermail","-w",
  1126. #  else
  1127.         "/bin/mail",
  1128. #  endif
  1129. #endif
  1130.         who,(char*)nil);
  1131.         VOID unlink(messagefile);
  1132.     return(true);
  1133. }
  1134.  
  1135.  
  1136.  
  1137. static breaklock(who,delta)
  1138. char * who; struct hshentry * delta;
  1139. /* function: Finds the lock held by who on delta,
  1140.  * and removes it.
  1141.  * Sends mail if a lock different from the caller's is broken.
  1142.  * Prints an error message if there is no such lock or error.
  1143.  */
  1144. {
  1145.         register struct lock * next, * trail;
  1146.         char * num;
  1147.         struct lock dummy;
  1148.         int whor, numr;
  1149.  
  1150.     num=delta->num;
  1151.         dummy.nextlock=next=Locks;
  1152.         trail = &dummy;
  1153.         while (next!=nil) {
  1154.         if (num != nil)
  1155.             numr = strcmp(num, next->delta->num);
  1156.  
  1157.         whor=strcmp(who,next->login);
  1158.         if (whor==0 && numr==0) break; /* exact match */
  1159.         if (numr==0 && whor !=0) {
  1160.                         if (!sendmail( num, next->login)){
  1161.                             diagnose("%s still locked by %s",num,next->login);
  1162.                 return;
  1163.                         } else break; /* continue after loop */
  1164.                 }
  1165.                 trail=next;
  1166.                 next=next->nextlock;
  1167.         }
  1168.         if (next!=nil) {
  1169.                 /*found one */
  1170.                 diagnose("%s unlocked",next->delta->num);
  1171.                 trail->nextlock=next->nextlock;
  1172.                 next->delta->lockedby=nil;
  1173.                 Locks=dummy.nextlock;
  1174.         } else  {
  1175.         error("no lock set on revision %s", num);
  1176.         }
  1177. }
  1178.  
  1179.  
  1180.  
  1181. struct hshentry *searchcutpt(object, length, store)
  1182. char    * object;
  1183. int     length;
  1184. struct  hshentry   * * store;
  1185. /*   Function:  Search store and return entry with number being object. */
  1186. /*              cuttail = nil, if the entry is Head; otherwise, cuttail */
  1187. /*              is the entry point to the one with number being object  */
  1188.  
  1189. {
  1190.         while( compartial( (*store++)->num, object, length)  )  ;
  1191.         store--;
  1192.  
  1193.         if ( *store == Head)
  1194.             cuthead = nil;
  1195.         else
  1196.             cuthead = *(store -1);
  1197.         return *store;
  1198. }
  1199.  
  1200.  
  1201.  
  1202. int  branchpoint(strt, tail)
  1203. struct  hshentry        *strt,  *tail;
  1204. /*   Function: check whether the deltas between strt and tail    */
  1205. /*        are locked or branch point, return 1 if any is  */
  1206. /*        locked or branch point; otherwise, return 0 and */
  1207. /*              mark DELETE on selector                         */
  1208.  
  1209. {
  1210.         struct  hshentry    *pt;
  1211.     struct lock  *lockpt;
  1212.         int     flag;
  1213.  
  1214.  
  1215.         pt = strt;
  1216.         flag = false;
  1217.         while( pt != tail) {
  1218.             if ( pt->branches ){ /*  a branch point  */
  1219.                 flag = true;
  1220.                 error("Can't remove branch point %s", pt->num);
  1221.             }
  1222.         lockpt = Locks;
  1223.         while(lockpt && lockpt->delta != pt)
  1224.         lockpt = lockpt->nextlock;
  1225.         if ( lockpt ) {
  1226.         flag = true;
  1227.         error("Can't remove locked revision %s",pt->num);
  1228.         }
  1229.             pt = pt->next;
  1230.         }
  1231.  
  1232.         if ( ! flag ) {
  1233.             pt = strt;
  1234.             while( pt != tail ) {
  1235.                 pt->selector = DELETE;
  1236.                 diagnose("deleting revision %s ",pt->num);
  1237.                 pt = pt->next;
  1238.             }
  1239.         }
  1240.         return flag;
  1241. }
  1242.  
  1243.  
  1244.  
  1245. removerevs()
  1246. /*   Function:  get the revision range to be removed, and place the     */
  1247. /*              first revision removed in delstrt, the revision before  */
  1248. /*              delstrt in cuthead( nil, if delstrt is head), and the   */
  1249. /*              revision after the last removed revision in cuttail(nil */
  1250. /*              if the last is a leaf                                   */
  1251.  
  1252. {
  1253.         struct  hshentry    *target, *target2, * temp, *searchcutpt();
  1254.         int     length, flag;
  1255.  
  1256.         flag = false;
  1257.         if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
  1258.         target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1259.         if ( ! target ) return 0;
  1260.         if ( cmpnum(target->num, &numrev[0]) ) flag = true;
  1261.         length = countnumflds( &numrev[0] );
  1262.  
  1263.         if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
  1264.         if ( length % 2)
  1265.         temp=searchcutpt(target->num,length+1,gendeltas);
  1266.         else if (flag) {
  1267.                 error("Revision %s does not exist", &numrev[0]);
  1268.         return 0;
  1269.         }
  1270.         else
  1271.         temp = searchcutpt(&numrev[0],length,gendeltas);
  1272.         cuttail = target->next;
  1273.             if ( branchpoint(temp, cuttail) ) {
  1274.                 cuttail = nil;
  1275.                 return 0;
  1276.             }
  1277.             delstrt = temp;     /* first revision to be removed   */
  1278.             return 1;
  1279.         }
  1280.  
  1281.         if ( length % 2 ) {   /*  invalid branch after -o   */
  1282.             error("Invalid branch range %s after -o", &numrev[0]);
  1283.             return 0;
  1284.         }
  1285.  
  1286.         if ( delrev->code == 1 )  {  /*  -o  -rev   */
  1287.             if ( length > 2 ) {
  1288.                 temp = searchcutpt( target->num, length-1, gendeltas);
  1289.                 cuttail = target->next;
  1290.             }
  1291.             else {
  1292.                 temp = searchcutpt(target->num, length, gendeltas);
  1293.                 cuttail = target;
  1294.                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
  1295.                     cuttail = cuttail->next;
  1296.             }
  1297.             if ( branchpoint(temp, cuttail) ){
  1298.                 cuttail = nil;
  1299.                 return 0;
  1300.             }
  1301.             delstrt = temp;
  1302.             return 1;
  1303.         }
  1304.  
  1305.         if ( delrev->code == 2 )  {   /*  -o  rev-   */
  1306.             if ( length == 2 ) {
  1307.                 temp = searchcutpt(target->num, 1,gendeltas);
  1308.                 if ( flag)
  1309.                     cuttail = target;
  1310.                 else
  1311.                     cuttail = target->next;
  1312.             }
  1313.             else  {
  1314.                 if ( flag){
  1315.                     cuthead = target;
  1316.                     if ( !(temp = target->next) ) return 0;
  1317.                 }
  1318.                 else
  1319.                     temp = searchcutpt(target->num, length, gendeltas);
  1320.                 getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
  1321.                 target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1322.             }
  1323.             if ( branchpoint( temp, cuttail ) ) {
  1324.                 cuttail = nil;
  1325.                 return 0;
  1326.             }
  1327.             delstrt = temp;
  1328.             return 1;
  1329.         }
  1330.  
  1331.         /*   -o   rev1-rev2   */
  1332.         if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
  1333.         if ( length != countnumflds( &numrev[0] ) ) {
  1334.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1335.             return 0;
  1336.         }
  1337.         if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
  1338.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1339.             return 0;
  1340.         }
  1341.  
  1342.         target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
  1343.         if ( ! target2 ) return 0;
  1344.  
  1345.         if ( length > 2) {  /* delete revisions on branches  */
  1346.             if ( cmpnum(target->num, target2->num) > 0) {
  1347.                 if ( cmpnum(target2->num, &numrev[0]) )
  1348.                     flag = true;
  1349.                 else
  1350.                     flag = false;
  1351.                 temp = target;
  1352.                 target = target2;
  1353.                 target2 = temp;
  1354.             }
  1355.             if ( flag ) {
  1356.                 if ( ! cmpnum(target->num, target2->num) ) {
  1357.                     error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
  1358.                     return 0;
  1359.                 }
  1360.                 cuthead = target;
  1361.                 temp = target->next;
  1362.             }
  1363.             else
  1364.                 temp = searchcutpt(target->num, length, gendeltas);
  1365.             cuttail = target2->next;
  1366.         }
  1367.         else { /*  delete revisions on trunk  */
  1368.             if ( cmpnum( target->num, target2->num) < 0 ) {
  1369.                 temp = target;
  1370.                 target = target2;
  1371.                 target2 = temp;
  1372.             }
  1373.             else
  1374.                 if ( cmpnum(target2->num, &numrev[0]) )
  1375.                     flag = true;
  1376.                 else
  1377.                     flag = false;
  1378.             if ( flag ) {
  1379.                 if ( ! cmpnum(target->num, target2->num) ) {
  1380.                     error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
  1381.                     return 0;
  1382.                 }
  1383.                 cuttail = target2;
  1384.             }
  1385.             else
  1386.                 cuttail = target2->next;
  1387.             temp = searchcutpt(target->num, length, gendeltas);
  1388.         }
  1389.         if ( branchpoint(temp, cuttail) )  {
  1390.             cuttail = nil;
  1391.             return 0;
  1392.         }
  1393.         delstrt = temp;
  1394.         return 1;
  1395. }
  1396.  
  1397.  
  1398.  
  1399. updateassoc()
  1400. /*   Function: add or delete(if revno is nil) association    */
  1401. /*        which is stored in assoclst            */
  1402.  
  1403. {
  1404.         struct  Symrev  * curassoc;
  1405.     struct  assoc   * pre,  * pt;
  1406.         struct  hshentry    * target;
  1407.  
  1408.         /*  add new associations   */
  1409.         curassoc = assoclst;
  1410.         while( curassoc ) {
  1411.             if ( curassoc->revno == nil ) {  /* delete symbol  */
  1412.         pre = pt = Symbols;
  1413.                 while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
  1414.             pre = pt;
  1415.             pt = pt->nextassoc;
  1416.         }
  1417.         if ( pt )
  1418.             if ( pre == pt )
  1419.             Symbols = pt->nextassoc;
  1420.             else
  1421.             pre->nextassoc = pt->nextassoc;
  1422.         else
  1423.                     warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
  1424.         }
  1425.             else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
  1426.         /*   add symbol  */
  1427.                target = (struct hshentry *) talloc(sizeof(struct hshentry));
  1428.                target->num = &numrev[0];
  1429.                VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
  1430.             }
  1431.             curassoc = curassoc->nextsym;
  1432.         }
  1433.  
  1434. }
  1435.  
  1436.  
  1437.  
  1438. updatelocks()
  1439. /* Function: remove lock for caller or first lock if unlockcaller==true;
  1440.  *           remove locks which are stored in rmvlocklst,
  1441.  *           add new locks which are stored in newlocklst,
  1442.  *           add lock for Dbranch or Head if lockhead==true.
  1443.  */
  1444. {
  1445.         struct  hshentry        *target;
  1446.         struct  Lockrev         *lockpt;
  1447.  
  1448.         if(unlockcaller == true) { /*  find lock for caller  */
  1449.             if ( Head ) {
  1450.         if (Locks) {
  1451.             target=findlock(caller,true);
  1452.             if (target==nil) {
  1453.             breaklock(caller, Locks->delta); /* remove most recent lock */
  1454.             } else {
  1455.             diagnose("%s unlocked",target->num);
  1456.             }
  1457.         } else {
  1458.             warn("There are no locks set.");
  1459.         }
  1460.             } else {
  1461.                 warn("Can't unlock an empty tree");
  1462.             }
  1463.         }
  1464.  
  1465.         /*  remove locks which are stored in rmvlocklst   */
  1466.         lockpt = rmvlocklst;
  1467.         while( lockpt ) {
  1468.         if (expandsym(lockpt->revno, numrev)) {
  1469.         target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1470.                 if ( target )
  1471.            if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
  1472.             error("Can't unlock nonexisting revision %s",lockpt->revno);
  1473.                    else
  1474.                         breaklock(caller, target);
  1475.                         /* breaklock does its own diagnose */
  1476.             }
  1477.             lockpt = lockpt->nextrev;
  1478.         }
  1479.  
  1480.         /*  add new locks which stored in newlocklst  */
  1481.         lockpt = newlocklst;
  1482.         while( lockpt ) {
  1483.             setlock(lockpt->revno,caller);
  1484.             lockpt = lockpt->nextrev;
  1485.         }
  1486.  
  1487.         if ( lockhead == true) {  /*  lock default branch or head  */
  1488.             if (Dbranch) {
  1489.                 setlock(Dbranch->num,caller);
  1490.             } elsif ( Head) {
  1491.                 if (addlock(Head, caller))
  1492.                     diagnose("%s locked",Head->num);
  1493.             } else {
  1494.                 warn("Can't lock an empty tree");
  1495.             }
  1496.         }
  1497.  
  1498. }
  1499.  
  1500.  
  1501.  
  1502. setlock(rev,who)
  1503. char * rev, * who;
  1504. /* Function: Given a revision or branch number, finds the correponding
  1505.  * delta and locks it for who.
  1506.  */
  1507. {
  1508.         struct  lock     *lpt;
  1509.         struct  hshentry *target;
  1510.  
  1511.         if (expandsym(rev, &numrev[0]) ){
  1512.             target = genrevs(&numrev[0],(char *) nil,(char *) nil,
  1513.                  (char *)nil, gendeltas);
  1514.             if ( target )
  1515.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
  1516.                     error("Can't lock nonexisting revision %s",numrev);
  1517.                else
  1518.                     if(lpt=addlock(target, who))
  1519.                         diagnose("%s locked",lpt->delta->num);
  1520.         }
  1521. }
  1522.  
  1523.  
  1524.  
  1525. rcs_setstate(rev,status)
  1526. char * rev, * status;
  1527. /* Function: Given a revision or branch number, finds the corresponding delta
  1528.  * and sets its state to status.
  1529.  */
  1530. {
  1531.         struct  hshentry *target;
  1532.  
  1533.         if ( expandsym(rev, &numrev[0]) ) {
  1534.             target = genrevs(&numrev[0],(char *) nil, (char *)nil,
  1535.                  (char *) nil, gendeltas);
  1536.             if ( target )
  1537.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
  1538.                     error("Can't set state of nonexisting revision %s to %s",
  1539.                            numrev,status);
  1540.                else
  1541.                     target->state = status;
  1542.         }
  1543. }
  1544.  
  1545.  
  1546.  
  1547.  
  1548.  
  1549. buildeltatext(deltas)
  1550. struct  hshentry        ** deltas;
  1551. /*   Function:  put the delta text on frewrite and make necessary   */
  1552. /*              change to delta text                                */
  1553. {
  1554.         int  i, c, exit_stats;
  1555.  
  1556.         cuttail->selector = DELETE;
  1557. #ifdef MSDOS
  1558.         initeditfiles( tmpdir );
  1559. #else
  1560.         initeditfiles("/tmp/");
  1561. #endif /* MSDOS */
  1562.         scanlogtext(deltas[0], copy);
  1563.         i = 1;
  1564.         if ( cuthead )  {
  1565. #ifdef MSDOS
  1566.             cutfilename=mktempfile( tmpdir, "ctXXXXXX");
  1567. #else
  1568.             cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
  1569. #endif /* MSDOS */
  1570.             if ( (fcut = fopen(cutfilename, "w")) == NULL) {
  1571.                 faterror("Can't open temporary file %s", cutfilename);
  1572.             }
  1573.  
  1574.             while( deltas[i-1] != cuthead )  {
  1575.                 scanlogtext(deltas[i++], edit);
  1576.             }
  1577.  
  1578.             finishedit((struct hshentry *)nil);    rewind(fcopy);
  1579.             while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
  1580.             swapeditfiles(false);
  1581.             ffclose(fcut);
  1582.         }
  1583.  
  1584.         while( deltas[i-1] != cuttail)
  1585.             scanlogtext(deltas[i++], edit);
  1586.         finishedit((struct hshentry *)nil);    ffclose(fcopy);
  1587.  
  1588.         if ( cuthead ) {
  1589. #ifdef MSDOS
  1590.             diffilename=mktempfile( tmpdir, "dfXXXXXX");
  1591. #else
  1592.             diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
  1593. #endif /* MSDOS */
  1594.             exit_stats = run((char*)nil,diffilename,
  1595. #ifdef MSDOS
  1596. #    ifdef GNUDIFF
  1597.             DIFF,"-n","-a",cutfilename,resultfile,(char*)nil);
  1598. #    elif MKS
  1599.             DIFF,"-n",cutfilename,resultfile,(char*)nil);
  1600. #    endif
  1601. #else
  1602.             DIFF,"-n",cutfilename,resultfile,(char*)nil);
  1603. #endif /* MSDOS */
  1604. #ifdef MSDOS
  1605.             if (exit_stats != 0 && exit_stats != 1 )
  1606. #else
  1607.             if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  1608. #endif /* MSDOS */
  1609.                 faterror ("diff failed");
  1610.             if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
  1611.         }
  1612.         else
  1613.             if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
  1614.  
  1615.         scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
  1616. }
  1617.  
  1618.  
  1619.  
  1620. buildtree()
  1621. /*   Function:  actually removes revisions whose selector field  */
  1622. /*              is DELETE, and rebuilds  the linkage of deltas.  */
  1623. /*              asks for reconfirmation if deleting last revision*/
  1624. {
  1625.     int c,  response;
  1626.  
  1627.     struct    hshentry   * Delta;
  1628.         struct  branchhead      *pt, *pre;
  1629.  
  1630.         if ( cuthead )
  1631.            if ( cuthead->next == delstrt )
  1632.                 cuthead->next = cuttail;
  1633.            else {
  1634.                 pre = pt = cuthead->branches;
  1635.                 while( pt && pt->hsh != delstrt )  {
  1636.                     pre = pt;
  1637.                     pt = pt->nextbranch;
  1638.                 }
  1639.                 if ( cuttail )
  1640.                     pt->hsh = cuttail;
  1641.                 else if ( pt == pre )
  1642.                     cuthead->branches = pt->nextbranch;
  1643.                 else
  1644.                     pre->nextbranch = pt->nextbranch;
  1645.             }
  1646.     else {
  1647.             if ( cuttail == nil && !quietflag) {
  1648.                 VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
  1649.         c = response = getchar();
  1650.         while( c != EOF && c != '\n') c = getchar();
  1651.                 if ( response != 'y' && response != 'Y') {
  1652.                     diagnose("No revision deleted");
  1653.             Delta = delstrt;
  1654.             while( Delta) {
  1655.             Delta->selector = 'S';
  1656.             Delta = Delta->next;
  1657.             }
  1658.             return;
  1659.         }
  1660.         }
  1661.             Head = cuttail;
  1662.     }
  1663.         return;
  1664. }
  1665.  
  1666.